﻿using System.Collections.Generic;
using System.Linq;
using Orchard.DisplayManagement.Descriptors;
using Orchard.DisplayManagement;
using System.IO;
using System.Web.Mvc;
using Orchard.Environment.Extensions;
using Orchard.Localization;

namespace So.CustomShapes.Shapes
{
    public class TableShape : IShapeTableProvider
    {
        #region Constructors

        public Localizer T { get; set; }

        public TableShape()
        {
            T = NullLocalizer.Instance;
        }

        #endregion Constructors

        #region Extensions

        /// <summary>
        /// Writes the THEAD element including the given TR and TH elements to the given Context
        /// </summary>
        /// <param name="output"></param>
        /// <param name="itemHeaders"></param>
        /// <param name="headingTagName"></param>
        /// <param name="rowTagName"></param>
        /// <param name="headingItemTagName"></param>
        /// <param name="headingTagAttributes"></param>
        /// <param name="headingItemAttributes"></param>
        /// <param name="rowTagAttributes"></param>
        /// <param name="headingTagClasses"></param>
        /// <param name="headingItemClasses"></param>
        /// <param name="rowTagClasses"></param>
        private void WriteHeading(
            TextWriter output,
            List<string> itemHeaders,
            string headingTagName = "thead",
            string rowTagName = "tr",
            string headingItemTagName = "th",
            Dictionary<string, string> headingTagAttributes = null,
            Dictionary<string, string> headingItemAttributes = null,
            Dictionary<string, string> rowTagAttributes = null,
            IEnumerable<string> headingTagClasses = null,
            string[] headingItemClasses = null,
            IEnumerable<string> rowTagClasses = null)
        {
            if (!itemHeaders.Any())
                return;

            var count = itemHeaders.Count();
            var index1 = 0;
            var headingTag = GetTagBuilder(headingTagName, null, headingTagClasses, headingTagAttributes);
            var rowTag = GetTagBuilder(rowTagName, null, rowTagClasses, rowTagAttributes);

            output.Write(headingTag.ToString(TagRenderMode.StartTag));
            output.Write(rowTag.ToString(TagRenderMode.StartTag));
            foreach (var item in itemHeaders)
            {
                var itemTagForTbody = GetTagBuilder(headingItemTagName, null, headingItemClasses, headingItemAttributes);
                if (index1 == 0)
                    itemTagForTbody.AddCssClass("first");
                if (index1 == count - 1)
                    itemTagForTbody.AddCssClass("last");
                output.Write(itemTagForTbody.ToString(TagRenderMode.StartTag));
                output.Write(T(item));
                output.Write(itemTagForTbody.ToString(TagRenderMode.EndTag));
                ++index1;
            }
            output.Write(rowTag.ToString(TagRenderMode.EndTag));
            output.Write(headingTag.ToString(TagRenderMode.EndTag));
        }

        /// <summary>
        ///  Writes the TBODY element including the given TR and TD elements to the given Context
        /// </summary>
        /// <param name="output"></param>
        /// <param name="display"></param>
        /// <param name="itemHeaders"></param>
        /// <param name="headingTagName"></param>
        /// <param name="rowTagName"></param>
        /// <param name="headingTagAttributes"></param>
        /// <param name="headingItemAttributes"></param>
        /// <param name="rowTagAttributes"></param>
        /// <param name="headingTagClasses"></param>
        /// <param name="headingItemClasses"></param>
        /// <param name="rowTagClasses"></param>
        private void WriteBody(
            TextWriter output,
            dynamic display,
            List<dynamic> itemHeaders,
            string headingTagName = "tbody",
            string rowTagName = "tr",
            Dictionary<string, string> headingTagAttributes = null,
            Dictionary<string, string> headingItemAttributes = null,
            Dictionary<string, string> rowTagAttributes = null,
            IEnumerable<string> headingTagClasses = null,
            string[] headingItemClasses = null,
            IEnumerable<string> rowTagClasses = null)
        {
            if (!itemHeaders.Any())
                return;

            var count = itemHeaders.Count();
            var index1 = 0;
            var headingTag = GetTagBuilder(headingTagName, null, headingTagClasses, headingTagAttributes);

            foreach (var item in itemHeaders)
            {
                var itemTagForTbody = GetTagBuilder(rowTagName, null, headingItemClasses, headingItemAttributes);
                if (index1 == 0)
                    itemTagForTbody.AddCssClass("first");
                if (index1 == count - 1)
                    itemTagForTbody.AddCssClass("last");
                output.Write(itemTagForTbody.ToString(TagRenderMode.StartTag));
                output.Write(display(item));
                output.Write(itemTagForTbody.ToString(TagRenderMode.EndTag));
                ++index1;
            }
            output.Write(headingTag.ToString(TagRenderMode.EndTag));
        }

        static TagBuilder GetTagBuilder(string tagName, string id, IEnumerable<string> classes, IDictionary<string, string> attributes)
        {
            var tagBuilder = new TagBuilder(tagName);
            tagBuilder.MergeAttributes(attributes, false);
            foreach (var cssClass in classes ?? Enumerable.Empty<string>())
                tagBuilder.AddCssClass(cssClass);
            if (!string.IsNullOrWhiteSpace(id))
                tagBuilder.GenerateId(id);
            return tagBuilder;
        }

        #endregion Extensions
        
        public void Discover(ShapeTableBuilder builder)
        {
            //todo add other attributes for thead and th element so we can do things like colspan etc.
            builder.Describe("Table").OnCreated(created =>
            {
                var list = created.Shape;
                list.Tag = "table";
                list.Classes = new List<string>();
                list.ItemClasses = new List<string>();
                list.ItemHeaders = new List<string>();
                list.ItemAttributes = new Dictionary<string, string>();
            });
        }

        [Shape]
        public void Table(
            dynamic Display,
            TextWriter Output,
            IEnumerable<dynamic> Items,
            IEnumerable<dynamic> TableHeadings,
            string Tag,
            string Id,
            IEnumerable<string> Classes,
            IDictionary<string, string> Attributes,
            IEnumerable<string> ItemClasses,
            IEnumerable<string> ItemHeaders,
            IDictionary<string, string> ItemAttributes)
        {
            if (Items == null)
                return;

            // avoid multiple enumeration stil using ienumerable as param
            Items = Items.ToList(); 
            
            var count = Items.Count();
            if (count < 1)
                return;
         
            var tableTag = GetTagBuilder(Tag, Id, Classes, Attributes);
            Output.Write(tableTag.ToString(TagRenderMode.StartTag));

            WriteHeading(Output, ItemHeaders.ToList());
            WriteBody(Output,Display, Items.ToList());
            //todo write <tfoot>

            Output.Write(tableTag.ToString(TagRenderMode.EndTag));
        }
    }
}
